/* ClassLoader.js
 *
 * Copyright 2006, Tim Dwelle.
 *
 * Licensed under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in
 * compliance with the License.  You may obtain a copy of
 * the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied.  See
 * the License for the specific language governing
 * permissions and limitations under the License.
 *
 */

//$package("dowry.loader");

/**
 * Dynamic classloader for loading Javascript classes based
 * on their fully-qualified classname.  Since Javascript is
 * a prototype-based programming language, the classloader
 * basically must "bolt on" the support for a class-based
 * idiom.  The classloader supports a Javascript class model
 * with the following features:
 *
 * <h3>Packages</h3>
 * <p>
 *      A package provides a unique namespace for organizing
 *      related functionality.  The package name can be used
 *      in conjunction with the namespace-relative name of
 *      the class to produce a fully-qualified classname.
 * </p>
 *
 * <h3>Imports</h3>
 * <p>
 *      By importing, you require that the imported class
 *      be loaded and avaiable for instances of the
 *      declaring class.  Unlike some languages, an import
 *      must always resolve to a discrete, fully-qualified
 *      classname; wildcards are not supported.
 * </p>
 *
 * <h3>Mixins</h3>
 * <p>
 *      Mixins provide a way to define groupings of related
 *      properties and/or methods that do not naturally
 *      express a class.  This related functionality can
 *      then be "mixed in" to a class, adding these
 *      properties and/or methods to its own.
 * </p>
 *
 * <h3>Composition</h3>
 * <p>
 *      The declared class will inherit the properties and
 *      methods of the superclass, then refine it with the
 *      specified class body.  If mixins are specified,
 *      then the class will add in any properties and
 *      methods of the mixin (as long as the mixin property
 *      or method does not conflict with the name of a
 *      property or method already defined by the class
 *      body, an ancestor class, or an already included
 *      mixin).
 * </p>
 *
 * <h3>Constructors</h3>
 * <p>
 *      If the class body contains a method matching the
 *      namespace-relative name of the class, this method
 *      will be used as the constructor.  If no constructor
 *      is explicitly defined, it will fall back to the
 *      superclass constructor.
 * </p>
 *
 * <h3>Instanced Methods</h3>
 * <p>
 *      In Javascript, functions are not evaluated based on
 *      the object they "live" on, but based on the current
 *      execution context.  This can cause all sorts of
 *      problems-- particularly when you attach a function
 *      to an event or pass a reference to a function, such
 *      that it will be called from a different execution
 *      context.  The classloader fixes the "this" problem
 *      by automatically instancing all of an object's
 *      methods... such that "this" will now always refer to
 *      the object on which the method lives.
 * </p>
 *
 * <h3>Overriding Methods</h3>
 * <p>
 *      The class body may override methods or constructors.
 *      However, you may still call the overriden ancestor
 *      method or constructor from within your new method by
 *      using <code>this.$super(arguments)</code>.
 * </p>
 *
 * <h3>Special Features</h3>
 * <p>
 *      In addition to <code>$super()</code>, every class
 *      instance contains the string property
 *      <code>$class</code> which contains the
 *      fully-qualified classname of the class, and the
 *      property <code>$classLoader</code> with a reference
 *      to the classloader that defined the class.
 * </p>
 *
 * <h3>Syntax</h3>
 * <p>
 *      The classloader exposes a "keyword" class syntax for
 *      defining classes in a natural style.  Actually, this
 *      is just a series of functions, but it allows for a
 *      much more natural way to declare a class. Example:
 * <p>
 *
 * <pre>
 *      $package("org.example.geom");
 *
 *      $mixin("GeomMath").$as(
 *      {
 *         // this could be generally useful,
 *         // so we put it in a mixin...
 *         // probably could come up with a
 *         // better name for this mixin,
 *         // though...
 *         PI : 3.14159
 *      });
 *
 *      // -------------------------
 *
 *      $package("org.example.geom");
 *
 *      $class("Shape").$as(
 *      {
 *         print : function()
 *         {
 *            return this.$class;
 *         }
 *
 *         // any other shared baseclass
 *         // stuff would go here too...
 *      });
 *
 *      // -------------------------
 *
 *      $package("org.example.geom");
 *
 *      $class("Circle").$extends("Shape").$with("GeomMath").$as(
 *      {
 *         radius : 0,
 *
 *         Circle : function(r)
 *         {
 *            this.radius = r;
 *         },
 *
 *         getArea : function()
 *         {
 *            return this.PI * this.radius * this.radius;
 *         },
 *
 *         print : function()
 *         {
 *            var s = this.super(arguments);
 *            var a = this.getArea();
 *            var r = this.radius;
 *
 *            return (s + ": radius=" + r + ",area=" + a);
 *         }
 *      });
 *
 * </pre>
 *
 */
//$class("ClassLoader").$as(
var DOWRY_LOADER_CLASSLOADER =
{
    /**
     * Constant placeholder object used with the
     * <code>callback()</code> function.
     *
     */
    VALUE : new Object(),

    /**
     * The parent classloader
     *
     */
    _parent : null,

    /**
     * The line where the eval() happens in the
     * parseClassDefinition() method.  THis is used to fix
     * exceptions which mistakenly think they happened where
     * they were eval()ed.
     *
     */
    _evalStmtLineNumber : null,

    /**
     * Map of class names to the URLs from which they were
     * loaded.  This is used to fix exceptions which
     * mistakenly think they happened where they were
     * eval()ed.
     *
     */
    _urls : null,

    /**
     * Creates a new classloader instance.
     *
     * @param parentClassLoader  the parent classloader
     *
     */
    ClassLoader : function(parentClassLoader)
    {
        this._parent = parentClassLoader;
        this._urls = new Object();
    },

    /**
     * Creates a class definition based on the provided
     * details.
     *
     * @param packageName  the package name for the class,
     *                     if needed to resolve a provided
     *                     namespace-relative class name
     *
     * @param importNames  an array of fully-qualified
     *                     classnames to be imported
     *
     * @param className    the fully-qualified or
     *                     namespace-relative name of the
     *                     class
     *
     * @param superName    the fully-qualified or
     *                     namespace-relative name of the
     *                     superclass
     *
     * @param mixinNames   an array of fully-qualified or
     *                     namespace-relatives names of
     *                     mixins to be mixed
     *
     * @param body         a map of features of the class
     *                     (properties and/or methods)
     *
     * @return             the class
     *
     */
    defineClass : function(packageName,
                           importNames,
                           className,
                           superName,
                           mixinNames,
                           body)
    {
        var c = null;

        if (className)
        {
            var classNodes = this._toNodes(className, packageName);
            var fullClassName = this._toFullName(classNodes);
            var shortClassName = this._toShortName(classNodes);

            var superClass =
                this._findSuper(superName, packageName, importNames);

            var superProto = this._toSuperProto(superClass);
            var fullSuperName = this._toSuperName(superProto);
            var superNodes = this._toNodes(fullSuperName);
            var shortSuperName = this._toShortName(superNodes);

            c = this._createClass(shortClassName);
            var proto = c.prototype;

            this._addSuper(proto, superProto);
            this._addMixins(proto, mixinNames, packageName, importNames);
            this._addBody(proto, body, superProto);
            this._addConstructor(proto, shortClassName, shortSuperName);
            this._addImports(proto, importNames);
            this._addClassName(proto, fullClassName);

            this._saveClass(c, classNodes);
        }

        return c;
    },

    /**
     * Creates a mixin definition based on the provided
     * details.
     *
     * @param packageName  the package name for the mixin,
     *                     if needed to resolve a provided
     *                     namespace-relative class name
     *
     * @param importNames  an array of fully-qualified
     *                     classnames to be imported
     *
     * @param mixinName    the fully-qualified or
     *                     namespace-relative name of the
     *                     mixin
     *
     * @param superName    the fully-qualified or
     *                     namespace-relative name of the
     *                     superclass
     *
     * @param body         a map of features of the mixin
     *                     (properties and/or methods)
     *
     * @return             the mixin
     *
     */
    defineMixin : function(packageName,
                           importNames,
                           mixinName,
                           superName,
                           body)
    {
        var m = null;

        if (mixinName)
        {
            var mixinNodes = this._toNodes(mixinName, packageName);
            var fullMixinName = this._toFullName(mixinNodes);

            var superMixin =
                this._findSuper(superName, packageName, importNames, true);

            m = this._createMixin();

            this._addSuper(m, superMixin);
            this._addImports(m, importNames);
            this._addBody(m, body, superMixin);
            this._addClassName(m, fullMixinName);

            this._saveClass(m, mixinNodes);
        }

        return m;
    },

    /**
     * Loads the class (or mixin) with the specified
     * fully-qualified class name. The default
     * implementation of this method searches for classes in
     * the following order:
     *
     * <ul>
     *  <li>  invoke <code>findLoadedClass()</code> to check
     *        if the class has already been loaded
     *  </li>
     *  <li>  invoke <code>loadClass()</code> on the parent
     *        classloader (if a parent classloader exists)
     *  </li>
     *  <li>  invoke <code>findClass()</code> to find the
     *        class
     *  </li>
     * </ul>
     *
     * @param className   the fully-qualified class name to
     *                    load
     *
     * @return            the class; or null, if not found
     *
     */
    loadClass : function(className)
    {
        var c = null;

        if (className)
        {
            c = this._findLoadedClass(className);

            if (!c && this._parent)
            {
                c = this._parent.loadClass(className);
            }

            if (!c)
            {
                c = this._findClass(className);
            }
        }

        return c;
    },

    /**
     * Adds the body (ie. defined properties and methods) to
     * the provided class.
     *
     * @param c           the class
     *
     * @param body        an object containing a map of the
     *                    class body
     *
     * @param superClass  the superclass, if applicable
     *
     */
    _addBody : function(c, body, superClass)
    {
        if (c && body)
        {
            for (var name in body)
            {
                if (name != "$mixin")
                {
                    c[name] = body[name];
                    if (typeof body[name] == "function")
                    {
                        c[name].callback = this._createCallback();
                        if (superClass)
                        {
                            c[name].$name = name;
                            c[name].$super = superClass;
                        }
                    }
                }
            }

            if (c.toString == null ||
                c.toString == Object.prototype.toString)
            {
                c.toString = this._createToString();
            }
        }
    },

    /**
     * Adds a reference to the fully-qualified class name
     * (<code>this.$class</code>) and the classloader that
     * loaded the class (<code>this.$classLoader</code>) to
     * the provided class.
     *
     * @param c           the class
     *
     * @param className   a string representing the
     *                    fully-qualified class name
     *
     */
    _addClassName : function(c, className)
    {
        if (c && className)
        {
            c.$class = className;
            c.$classLoader = this;
        }
    },

    /**
     * Adds/fixes the constructor method if one does not
     * already exist, since the superclass constructor
     * likely has a different name than the child's
     * constructor.
     *
     * @param c           the class
     *
     * @param className   the short class name, which will
     *                    also be the name of the
     *                    constructor method
     *
     * @param superName   the short superclass name, which
     *                    is also the name of the
     *                    superclass's constructor method
     *
     */
    _addConstructor : function(c, className, superName)
    {
        if (c && className)
        {
            if (!c[className])
            {
                if (c[superName])
                {
                    c[className] = c[superName];
                }
                else
                {
                    c[className] = function() {};
                }
            }
            else
            {
                c[className].$name = superName;
            }

            c[className].$constructor = true;
        }
    },

    /**
     * Adds import references to the provided class.  Since
     * imports may present circular references, we just
     * capture the list of imports when defining the class,
     * and wait to actually do the import of these classes
     * when we instantiate.
     *
     * @param c                the class
     *
     * @param importNames      array of fully-qualified
     *                         class names to be imported
     *
     */
    _addImports : function(c, importNames)
    {
        if (c && importNames)
        {
            if (!c.$importNames)
            {
                c.$importNames = new Array();
            }

            c.$importNames = c.$importNames.concat(importNames);
        }
    },

    /**
     * Adds requested mixins to the provided class.  A
     * mixin property or method will only be "mixed in" to
     * the class if it does not conflict with a property or
     * method already defined in an ancestor's class body
     * or a previous mixin's body.
     *
     * @param c            the class
     *
     * @param mixinNames   an array of fully-qualified or
     *                     namespace-relatives names of
     *                     mixins to be mixed
     *
     * @param packageName  the package name for the class,
     *                     if needed to resolve a provided
     *                     namespace-relative mixin name
     *
     * @param importNames  an array of fully-qualified
     *                     classnames to be imported
     *
     */
    _addMixins : function(c, mixinNames, packageName, importNames)
    {
        if (c && mixinNames)
        {
            var m = null;
            var mixinName = null;
            for (var i=0; i < mixinNames.length; i++)
            {
                mixinName = mixinNames[i];
                if (mixinName)
                {
                    m = this._findSuper(mixinName,
                                        packageName,
                                        importNames,
                                        true);

                    if (m)
                    {
                        for (var name in m)
                        {
                            if (typeof c[name] == "undefined")
                            {
                                c[name] = m[name];
                            }
                        }
                    }
                }
            }
        }
    },

    /**
     * Adds the <code>this.$super</code> method to the
     * provided class, allowing you to call the parent of
     * an overriden method.
     *
     * @param c                the class
     *
     * @param superClass       the superclass
     *
     */
    _addSuper : function(c, superClass)
    {
        if (c && superClass)
        {
            for (var name in superClass)
            {
                c[name] = superClass[name];
            }

            if (window.$classLoader)
            {
                c.$super = dowry.loader.ClassLoader.prototype._super;
            }
        }
    },

    /**
     * Generates a callback function.  The first argument
     * should be the function to be called, followed by any
     * arguments to that function.  If you want the callback
     * function to be called with the return value as one of
     * its arguments, then include the special variable
     * <code>$VALUE</code> as a placeholder where you want
     * the return value included in the argument list. For
     * instance:
     *
     * <pre>
     *       var func = loader._callback(myFunc,
     *           ["foo", $VALUE, "bar", "baz"]);
     * </pre>
     *
     * <p>
     * will produce a function that is equivalent to:
     * </p>
     *
     * <pre>
     *       function(val)
     *       {
     *          myFunc("foo", val, "bar", "baz");
     *       }
     * </pre>
     *
     * <p>
     * For simpler syntax, any instance of a class loaded
     * via the classloader automatically has this
     * <code>callback()</code> function bound to each of
     * the object's methods.  So, typically a callback will
     * look like the following:
     * </p>
     *
     * <pre>
     *       myObj.myFunc.callback("foo", $VALUE, "bar", "baz");
     * </pre>
     *
     *
     * @param func   the function to call
     *
     * @param args   array of args to pass to the function
     *
     * @return       a callback function that calls the
     *               specified function with the appropriate
     *               arguments
     *
     */
    _callback : function(func, args)
    {
        return function(val)
        {
            for (var i=0; i < args.length; i++)
            {
                if (args[i] == $VALUE)
                {
                    args[i] = val;
                }
            }

            var retVal = func.apply(null, args);

            args = null;
            val = null;

            return retVal;
        }
    },

    /**
     * Part of the "keyword" class syntax used to support
     * declaring classes.  Example:
     *
     * <pre>
     * $package("org.example");
     *
     * $import("org.example.util.Baz");
     * $import("org.example.util.Bubba");
     *
     * $class("Foo").$extends("Bar").$with("Baz", "Bubba").$as(
     * {
     *    name : null,
     *
     *    Foo : function(name)
     *    {
     *       // call the constructor
     *       // of the superclass
     *       this.$super(arguments);
     *
     *       // set an instance variable
     *       this.name = name;
     *    },
     *
     *    hello : function()
     *    {
     *       return "Hello, " + this.name;
     *    }
     * });
     * </pre>
     *
     * @param className    the fully-qualified or
     *                     namespace-relative name of the
     *                     class
     *
     * @return             an object to continue the
     *                     class declaration using the
     *                     "keyword" methods
     *                     <code>$extends</code>,
     *                     <code>$with</code>, and
     *                     <code>$as</code>
     *
     */
    _class : function(className)
    {
        var extendsObj =
        {
            $extends : /*js2javadoc:ignore*/function(superName)
            {
                var withObj =
                {
                    $with : /*js2javadoc:ignore*/function()
                    {
                        var mixinNames = null;
                        if (arguments.length > 0)
                        {
                            mixinNames = new Array();
                            for (var i=0; i < arguments.length; i++)
                            {
                                if (arguments[i])
                                {
                                    if (!mixinNames)
                                    {
                                        mixinNames = new Array();
                                    }
                                    mixinNames[i] = arguments[i];
                                }
                            }
                        }

                        var asObj =
                        {
                            $as : /*js2javadoc:ignore*/function(body)
                            {
                                var packageName = null;
                                var importNames = null;
                                if ($class.$ns)
                                {
                                    packageName = $class.$ns.packageName;
                                    importNames = $class.$ns.importNames;
                                    $class.$ns = null;
                                }

                                $classLoader.defineClass(packageName,
                                                         importNames,
                                                         className,
                                                         superName,
                                                         mixinNames,
                                                         body);
                            }
                        };

                        return asObj;
                    },

                    $as : /*js2javadoc:ignore*/function(body)
                    {
                        $class(className)
                            .$extends(superName)
                            .$with(null)
                            .$as(body);
                    }
                };

                return withObj;
            },

            $with : /*js2javadoc:ignore*/function()
            {
                var asObj = null;

                if (arguments.length > 0)
                {
                    asObj = $class(className)
                        .$extends(null)
                        .$with
                        .apply(null, arguments);
                }
                else
                {
                    asObj = $class(className).$extends(null).$with(null);
                }

                return asObj;
            },

            $as : /*js2javadoc:ignore*/function(body)
            {
                $class(className).$extends(null).$with(null).$as(body);
            }
        };

        return extendsObj;
    },

    /**
     * Creates the callback function to attach to the
     * method via <code>method.callback()</code>.
     *
     * @return  the callback function
     *
     */
    _createCallback : function()
    {
        return function()
        {
            return dowry.loader.ClassLoader.prototype._callback(this,
                                                                arguments);
        }
    },

    /**
     * Creates the class implementation, such that when a
     * new instance of the class is created, the constructor
     * method will be called.
     *
     * @param constructorName  the string name of the
     *                         constructor method (this
     *                         should be the same as the
     *                         short class name)
     *
     * @return                 the "raw" class function
     *
     */
    _createClass : function(constructorName)
    {
        return function()
        {
            for (var name in this)
            {
                if (name != "$super" && typeof this[name] == "function")
                {
                    this[name] = this.$classLoader._toMethod(this,
                                                             this[name],
                                                             name);
                }
            }

            this.$classLoader._loadImports(this, this.$importNames);
            delete this.$importNames;

            this[constructorName].apply(this, arguments);
        }
    },

    /**
     * Creates the mixin object.
     *
     * @return   the "raw" mixin object
     *
     */
    _createMixin : function()
    {
        var m = new Object();
        m.$mixin = true;
        return m;
    },

    /**
     * Creates a default <code>toString()</code> method for
     * the object that returns in the format
     * <code>[object {fully-qualified classname}]</code>.
     *
     */
    _createToString: function()
    {
        return function()
        {
            return "[object "+this.$class+"]";
        }
    },

    /**
     * Finds the class with the specified fully-qualfiied
     * class name.  This default implementation returns
     * null.  However, more sophisticated classloader
     * implementations may wish to override this method to
     * provide some form of dynamic class loading (perhaps
     * by loading the class from the server via an ajax
     * call).
     *
     * @param className   the fully-qualified class name to
     *                    load
     *
     * @return            the class; or null, if not found
     *
     */
    _findClass : function(className)
    {
        return null;
    },

    /**
     * Finds the class with the specified fully-qualified
     * class name by checking if the class already exists
     * under the classloader's root namespace hierarchy.
     *
     * @param className   the fully-qualified class name to
     *                    load
     *
     * @return            the class; or null, if not found
     *
     */
    _findLoadedClass : function(className)
    {
        var o = window;
        if (className)
        {
            var nodes = this._toNodes(className);

            var node = null;
            for (var i=0; i < nodes.length; i++)
            {
                node = nodes[i];
                o = o[node];
                if (!o)
                {
                    break;
                }
            }
        }

        return o;
    },

    /**
     * Gets the superclass instance based on the specified
     * declaration.  If the superclass name is not qualified
     * with a namespace prefix, then the superclass will be
     * searched for in the following order:
     *
     * <ol>
     *  <li>  a match of one of the declared imports
     *  </li>
     *  <li>  the class's package name plus the name of the
     *        superclass
     *  </li>
     *  <li>  the superclass name, in the root package
     *  </li>
     * </ol>
     *
     * @param superName    the fully-qualified or
     *                     namespace-relative name of the
     *                     superclass
     *
     * @param packageName  the package name for the class,
     *                     if needed to resolve a provided
     *                     namespace-relative superclass
     *                     name
     *
     * @param importNames  array of fully-qualified class
     *                     names to be imported
     *
     * @param mixin        optional flag indicating that the
     *                     parent should be a mixin
     *
     * @return             the superclass
     *
     */
    _findSuper : function(superName, packageName, importNames, mixin)
    {
        var c = null;

        if (superName)
        {
            var nodes = this._toNodes(superName);

            if (nodes.length > 1)
            {
                c = this.loadClass(superName);
            }
            else
            {
                if (importNames)
                {
                    for (var i=0; i < importNames.length; i++)
                    {
                        var importName = importNames[i];
                        importName = this._toNodes(importName);
                        importName = this._toShortName(importName);

                        if (superName == importName)
                        {
                            c = this.loadClass(importNames[i]);
                            break;
                        }
                    }
                }

                if (!c && packageName)
                {
                    c = this.loadClass(packageName + "." + superName);
                }

                if (!c)
                {
                    c = this.loadClass(superName);
                }
            }

            if (!c)
            {
                throw new TypeError(superName);
            }
            else if (mixin && !c.$mixin)
            {
                throw new TypeError(superName);
            }
        }

        return c;
    },

    /**
     * Since the exception to include the class name,
     * method name, line number, stack trace, etc.
     * appropriate for the class definition.
     *
     * @param e           the exception
     *
     * @param o           the object instance that generated
     *                    the exception
     *
     * @param methodName  then name of the method that
     *                    threw the exception
     *
     * @return            the "fixed" exception
     *
     */
    _fixException : function(e, o, methodName)
    {
        if (!e.className)
        {
            // class
            e.className = o.$class;

            // method
            if (methodName)
            {
                e.methodName = methodName;
            }

            // file
            if (o.$classLoader._urls[o.$class])
            {
                e.fileName = o.$classLoader._urls[o.$class];
            }

            if (e.lineNumber && o.$class)
            {
                if (!this._evalStmtLineNumber)
                {
                    // we need to generate an error so
                    // we can find out where that eval
                    // stmt happens
                    try
                    {
                        dowry.loader.ClassLoader.prototype.
                            _parseClassDefinition("SyntaxError", "a-");
                    }
                    catch(evalStmt)
                    {
                        if (evalStmt.lineNumber)
                        {
                            this._evalStmtLineNumber = evalStmt.lineNumber-1;
                        }

                    }
                }

                // line number
                if (this._evalStmtLineNumber)
                {
                    e.lineNumber -= this._evalStmtLineNumber;

                    // stack trace
                    if (e.stack)
                    {
                        e.stack = "@"+e.fileName+
                                  ":"+e.lineNumber+
                                  "\n"+e.stack;
                    }
                }
            }

            // message
            if (e.message)
            {
                e.message += " (" + e.className;
                if (e.methodName)
                {
                    e.message += "." + e.methodName;
                }
                if (e.lineNumber)
                {
                    e.message += ":" + e.lineNumber;
                }
                e.message += ")";
            }
        }

        return e;
    },

    /**
     * Part of the "keyword" class syntax used to support
     * specifying an import statement for a class or mixin.
     * By importing, you require that the imported class
     * be loaded and avaiable for instances of the declaring
     * class.  Unlike some languages, an import must always
     * resolve to a discrete, fully-qualified classname;
     * wildcards are not supported.
     *
     * @param importName   the fully-qualified classname to
     *                     be imported
     *
     */
    _import : function(importName)
    {
        if (!$class.$ns)
        {
            $class.$ns = new Object();
        }
        if (!$class.$ns.importNames)
        {
            $class.$ns.importNames = new Array();
        }

        $class.$ns.importNames[$class.$ns.importNames.length] = importName;
    },

    /**
     * Loads the classes specified in the array of imports,
     * and stores them on the provided object, such that the
     * key is the short class name preceded by a '$' and the
     * value is the class itself.  If multiple imported
     * classes share the same short class name, only the
     * first will be added to the object.  However, all of
     * the specified classes will be loaded.
     *
     * @param o            the object instance
     *
     * @param importNames  array of fully-qualified class
     *                     names to be imported
     *
     */
    _loadImports : function(o, importNames)
    {
        if (o && importNames)
        {
            var c = null;
            var importName = null;
            for (var i=0; i < importNames.length; i++)
            {
                importName = importNames[i];
                if (importName)
                {
                    c = this.loadClass(importName);
                    if (!c)
                    {
                        throw new TypeError(importName);
                    }
                }
            }
        }

        return o;
    },

    /**
     * Part of the "keyword" class syntax used to support
     * declaring mixins.  A mixin provides a way to define
     * groupings of related properties and/or methods that
     * do not naturally express a class.  This related
     * functionality can then be "mixed in" to a class,
     * adding these properties and/or methods to its own.
     * Example:
     *
     * <pre>
     * $package("org.example.util");
     *
     * $mixin("Baz").$as(
     * {
     *    add : function(x, y)
     *    {
     *       return x + y;
     *    },
     *
     *    subtract : function(x, y)
     *    {
     *       return x - y;
     *    }
     * });
     * </pre>
     *
     *
     * @param mixinName    the fully-qualified or
     *                     namespace-relative name of the
     *                     class
     *
     * @return             an object to continue the
     *                     mixin declaration using the
     *                     "keyword" methods
     *                     <code>$extends</code> and
     *                     <code>$as</code>
     *
     */
    _mixin : function(mixinName)
    {
        var extendsObj =
        {
            $extends : /*js2javadoc:ignore*/function(superName)
            {
                var asObj =
                {
                    $as : /*js2javadoc:ignore*/function(body)
                    {
                        var packageName = null;
                        var importNames = null;
                        if ($class.$ns)
                        {
                            packageName = $class.$ns.packageName;
                            importNames = $class.$ns.importNames;
                            $class.$ns = null;
                        }

                        $classLoader.defineMixin(packageName,
                                                 importNames,
                                                 mixinName,
                                                 superName,
                                                 body);
                    }
                };

                return asObj;
            },

            $as : /*js2javadoc:ignore*/function(body)
            {
                $mixin(mixinName).$extends(null).$as(body);
            }
        };

        return extendsObj;
    },

    /**
     * Part of the "keyword" class syntax used to support
     * specifying a package statement for a class or mixin.
     * A package provides a unique namespace for organizing
     * related functionality.  The package name is combined
     * with the namepsace-relative name of the class to
     * produce the fully-qualified classname.
     *
     * @param packageName  the package name for the class,
     *                     if needed to resolve a provided
     *                     namespace-relative class name
     *
     */
    _package : function(packageName)
    {
        if (!$class.$ns)
        {
            $class.$ns = new Object();
        }

        $class.$ns.packageName = packageName;
    },

    /**
     * Binds the class under the classloader's root
     * namespace.
     *
     * @param c      the class
     *
     * @param nodes  the fully-qualified class name
     *               expressed as an array of nodes
     *
     */
    _saveClass : function(c, nodes)
    {
        var o = window;

        if (c && nodes)
        {
            var len = nodes.length-1;

            var node = null;
            for (var i=0; i < len; i++)
            {
                node = nodes[i];
                if (!o[node])
                {
                    o[node] = new Object();
                }
                o = o[node];
            }

            node = nodes[len];
            o[node] = c;
        }

        return o;
    },

    /**
     * When you override the constructor or any method on a
     * class, you can still get the ancestor's method by
     * using <code>this.$super(arguments)</code> (or
     * alternately, an array containing the appropriate
     * custom arguments).  This method implements that
     * feature.
     *
     * @param  a   the <code>arguments</code> collection
     *             passed into the original method, or
     *             alternately, an array containing the
     *             appropriate custom arguments
     *
     * @return     the value returned from calling the
     *             ancestor's method
     *
     */
    _super : function(a)
    {
        var func = null;

        if (a && a.callee)
        {
            func = a.callee;
        }
        else if (arguments && arguments.callee && arguments.callee.caller)
        {
            func = arguments.callee.caller;
        }

        if (func && func.$name && func.$super)
        {
            func = func.$super[func.$name];
            return func.apply(this, a);
        }
    },

    /**
     * Gets the fully-qualified class name as a string.
     *
     * @param nodes  the fully-qualified class name
     *               expressed as an array of nodes
     *
     * @return       a string representing the
     *               fully-qualified class name
     *
     */
    _toFullName : function(nodes)
    {
        var s = null;

        if (nodes)
        {
            s = nodes.join(".");
        }

        return s;
    },

    /**
     * Makes the provided function into a method instanced
     * to the specified object, such that <code>this</code>
     * always refers to the object on which the method
     * resides.
     *
     * <p>
     * In his Base framework, Dean Edwards replaces the
     * <code>valueOf()</code> and <code>toString()</code> on
     * the wrapped method.  This seems a pretty clever
     * workaround so our instancing doesn't "hide" our
     * original function.
     * </p>
     *
     * @param o     the object instance
     *
     * @param func  the function
     *
     * @param name  (optional) the function name, used for
     *              debugging
     *
     * @return      a method that is instanced to the
     *              object
     *
     */
    _toMethod : function(o, func, name)
    {
        var f = function()
        {
            try
            {
                return func.apply(o, arguments);
            }
            catch(e)
            {
                throw o.$classLoader._fixException(e, o, name);
            }
        }

        f.valueOf = function()
        {
            return func;
        }

        f.toString = function()
        {
            return func.toString();
        }

        f.callback = this._createCallback();

        return f;
    },

    /**
     * Gets the fuly-qualfieid class name expressed as an
     * array of nodes, representing each node in the
     * namespace.  If a package name is provided, this will
     * correctly resolve any namepsace-relative class name
     * based on the class's package name.
     *
     * @param className    the fully-qualified or
     *                     namespace-relative name of the
     *                     class
     *
     * @param packageName  the package name for the class,
     *                     if needed to resolve a provided
     *                     namespace-relative class name
     *
     * @return             the fully-qualified class name
     *                     expressed as an array of nodes
     *
     */
    _toNodes : function(className, packageName)
    {
        var nodes = null;

        if (className)
        {
            nodes = className.split(".");

            if (packageName && nodes.length == 1)
            {
                nodes = packageName.split(".");
                nodes[nodes.length] = className;
            }
        }

        return nodes;
    },

    /**
     * Gets the short, namespace-relative name of the class.
     *
     * @param nodes  the fully-qualified class name
     *               expressed as an array of nodes
     *
     * @return       a string representing the the short,
     *               namespace-relative name of the class
     *
     */
    _toShortName : function(nodes)
    {
        var s = null;

        if (nodes && nodes.length > 0)
        {
            s = nodes[nodes.length-1];
        }

        return s;
    },

    /**
     * Gets the prototype of the superclass.
     *
     * @param superClass  the superclass
     *
     * @return            the prototype of the superclass
     *
     */
    _toSuperProto : function(superClass)
    {
        var proto = null;

        if (superClass)
        {
            proto = superClass.prototype;
        }

        return proto;
    },

    /**
     * Gets the fully-qualified class name of the
     * superclass.
     *
     * @param superClass  the superclass
     *
     * @return            a string representing the
     *                    fully-qualified class name of the
     *                    superclass
     *
     */
    _toSuperName : function(superClass)
    {
        var s = null;

        if (superClass)
        {
            s = superClass.$class;
        }

        return s;
    },


    /**
     * Parses the raw Javascript class definition into a
     * class.  This method is not used by the base
     * classloader itself, but is provided as a convenience
     * for classloader implementers.
     *
     * @param className   the fully-qualified class name to
     *                    load
     *
     * @param js          the raw Javascript containing the
     *                    class definition, as a string
     *
     * @param url         (optional) the url where the class
     *                    definition can be found
     *
     * @return            the class; or null, if the class
     *                    cannot be loaded from this
     *                    definition
     *
     */
    _parseClassDefinition : function(className, js, url)
    {
        var c = null;

        if (className && js)
        {
            eval(js);
            c = this._findLoadedClass(className);

            if (c && url)
            {
                this._urls[className] = url;
            }
        }

        return c;
    },

    /**
     * In order to create the <code>ClassLoader</code>
     * class, we need to bootstrap the initial instance.
     * This method is only available during boostrapping,
     * after which it cleans itself up.  This method also
     * exports the "keyword" class syntax for
     * <code>$classLoader</code>, <code>$package</code>,
     * <code>$import</code>, <code>$class</code>,
     * <code>$mixin</code>, and <code>$VALUE</code> as
     * globals.
     *
     */
    _bootstrap : function()
    {
        var o = DOWRY_LOADER_CLASSLOADER;
        var s = "dowry.loader.ClassLoader";

        delete DOWRY_LOADER_CLASSLOADER;
        delete o._bootstrap;

        var ClassLoader = o.defineClass(null, null, s, null, null, o);
        var loader = new ClassLoader();

        window.$classLoader = loader;
        window.$package = loader._package;
        window.$import = loader._import;
        window.$class = loader._class;
        window.$mixin = loader._mixin;
        window.$VALUE = loader.VALUE;
    }
}

DOWRY_LOADER_CLASSLOADER._bootstrap();